﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Linq;
using System.Reflection;

namespace Kocic.RepositoryManagement.LinqToSql
{
    /// <summary>
    /// Allows data managers to re-use a single instance of an object context for LINQ to SQL.
    /// </summary>
    /// <typeparam name="TContext">The type of the entity data context to use within execution context this instance.</typeparam>
    public class LinqToSqlExecutionContext<TContext> : DataSourceExecutionContext<TContext>
        where TContext : DataContext, new()
    {
        /// <summary>
        /// Creates an instance of LinqToSqlContextScope class.
        /// </summary>
        public LinqToSqlExecutionContext()
        {

        }

        /// <summary>
        /// Creates an instance of LinqToSqlContextScope class.
        /// </summary>
        /// <param name="connectionString">The database connection string used to initalize a connection to the database.</param>
        /// <exception cref="ArgumentNullException"></exception>
        public LinqToSqlExecutionContext(string connectionString)
            : base(connectionString)
        {
            if (string.IsNullOrEmpty(connectionString))
            {
                throw new ArgumentNullException("connectionString", "Connection string cannot be null or empty.");
            }

            this.DataString = connectionString;
        }

        /// <summary>
        /// Initializes the data context by utilizing reflection to pass in the database connection string to the constructor of the instance of TContext.
        /// </summary>
        protected override void InitializeDataSource()
        {
            //Gets the type of the generic context defined. Uses reflection to invoke the parameterized constructor to pass in the connection string needed to connect to CRM.
            Type t = typeof(TContext);
            ConstructorInfo contextConstructor = t.GetConstructor(new Type[] { typeof(string) });
            object contextClassObject = contextConstructor.Invoke(new object[] { this.DataString });

            this.DataSource = contextClassObject as TContext;

            ExecutionContext = this;
        }

        /// <summary>
        /// Disposes unmanaged resources.
        /// </summary>
        /// <param name="disposing">Value indicating if to invoke the method from the IDisposable.Dispose implementation or from the finalizer.</param>
        protected override void Dispose(Boolean disposing)
        {
            if (disposing)
            {
                if (this.Disposed)
                {
                    return;
                }

                //Set static member reference to null
                ExecutionContext = null;

                try
                {
                    if (this.DataSource.Connection.State == System.Data.ConnectionState.Open)
                    {
                        this.DataSource.Connection.Close();
                        this.DataSource.Dispose();
                    }
                }
                finally
                {
                    base.Dispose(disposing);
                }
            }
        }
    }
}
